using System;
using System.Collections.Generic;
using UnityEngine;


namespace TrueSync
{
	public abstract class AbstractLockstep
	{
		private enum SimulationState // 模拟状态
		{
            NOT_STARTED, // 未启动
			WAITING_PLAYERS, // 等待玩家
			RUNNING, // 运行中
			PAUSED, // 暂停
			ENDED // 结束
		}
        
		private const int INITIAL_PLAYERS_CAPACITY = 4; // 初始玩家数量

		private const byte SYNCED_GAME_START_CODE = 196; // 开始同步

		private const byte SIMULATION_CODE = 197; // 模拟帧

		private const byte CHECKSUM_CODE = 198; // 校验

		private const byte SEND_CODE = 199;

		private const byte SIMULATION_EVENT_PAUSE = 0;

		private const byte SIMULATION_EVENT_RUN = 1;

		private const byte SIMULATION_EVENT_END = 3;

		private const int MAX_PANIC_BEFORE_END_GAME = 5;

		private const int SYNCED_INFO_BUFFER_WINDOW = 3;
        
		internal Dictionary<byte, TSPlayer> players;

		internal List<TSPlayer> activePlayers;

		internal List<SyncedData> auxPlayersSyncedData;

		internal List<InputDataBase> auxPlayersInputData;

        private List<int> playersIdsAux = new List<int>(); // 非本地玩家的活跃玩家ID列表
		internal int[] auxActivePlayersIds; // 非本地玩家的活跃玩家ID数组

		internal TSPlayer localPlayer; // 本地玩家

		protected TrueSyncUpdateCallback StepUpdate;

		private TrueSyncInputCallback GetLocalData; // 获取本地数据

		internal TrueSyncInputDataProvider InputDataProvider;

		private TrueSyncEventCallback OnGameStarted;

		private TrueSyncEventCallback OnGamePaused;

		private TrueSyncEventCallback OnGameUnPaused;

		private TrueSyncEventCallback OnGameEnded;

		private TrueSyncPlayerDisconnectionCallback OnPlayerDisconnection;

		public TrueSyncIsReady GameIsReady;

		protected int ticks;

		private int panicWindow;

		protected int syncWindow;

		private int elapsedPanicTicks;

		private AbstractLockstep.SimulationState simulationState;

		internal int rollbackWindow; // 回滚窗口数

		internal ICommunicator communicator;

		protected IPhysicsManagerBase physicsManager;

		private GenericBufferWindow<SyncedInfo> bufferSyncedInfo;

		protected int totalWindow;

		public bool checksumOk;

		public CompoundStats compoundStats;

		public float deltaTime;

		public int _lastSafeTick = 0;

		protected Dictionary<int, List<IBody>> bodiesToDestroy;

		protected Dictionary<int, List<byte>> playersDisconnect;

		private ReplayMode replayMode; // 重放模式

		private ReplayRecord replayRecord; // 重放记录

		internal static AbstractLockstep instance;

		private SyncedData[] _syncedDataCacheDrop = new SyncedData[1];

		private SyncedData[] _syncedDataCacheUpdateData = new SyncedData[1];

        #region 属性
        public List<TSPlayer> ActivePlayers
		{
			get
			{
				return this.activePlayers;
			}
		}

		public IDictionary<byte, TSPlayer> Players
		{
			get
			{
				return this.players;
			}
		}

		public TSPlayer LocalPlayer
		{
			get
			{
				return this.localPlayer;
			}
		}

		public int Ticks
		{
			get
			{
				return this.GetSimulatedTick(this.GetSyncedDataTick()) - 1;
			}
		}

		public int LastSafeTick
		{
			get
			{
				int result;
				if (this._lastSafeTick < 0)
				{
					result = -1;
				}
				else
				{
					result = this._lastSafeTick - 1;
				}
				return result;
			}
		}

		private ReplayMode ReplayMode
		{
			set
			{
				this.replayMode = value;
				if (this.replayMode == ReplayMode.RECORD_REPLAY)
				{
					this.replayRecord = new ReplayRecord();
				}
			}
		}

		public ReplayRecord ReplayRecord
		{
			set
			{
				this.replayRecord = value;
				if (this.replayRecord != null)
				{
					this.replayMode = ReplayMode.LOAD_REPLAY;
					this.replayRecord.ApplyRecord(this);
				}
			}
		}
        #endregion 属性

        public static AbstractLockstep NewInstance(float deltaTime, ICommunicator communicator, IPhysicsManagerBase physicsManager, int syncWindow, int panicWindow, int rollbackWindow, TrueSyncEventCallback OnGameStarted, TrueSyncEventCallback OnGamePaused, TrueSyncEventCallback OnGameUnPaused, TrueSyncEventCallback OnGameEnded, TrueSyncPlayerDisconnectionCallback OnPlayerDisconnection, TrueSyncUpdateCallback OnStepUpdate, TrueSyncInputCallback GetLocalData, TrueSyncInputDataProvider InputDataProvider)
		{
			AbstractLockstep result;
			if (rollbackWindow <= 0 || communicator == null)
			{
				result = new DefaultLockstep(deltaTime, communicator, physicsManager, syncWindow, panicWindow, rollbackWindow, OnGameStarted, OnGamePaused, OnGameUnPaused, OnGameEnded, OnPlayerDisconnection, OnStepUpdate, GetLocalData, InputDataProvider);
			}
			else
			{
				result = new RollbackLockstep(deltaTime, communicator, physicsManager, syncWindow, panicWindow, rollbackWindow, OnGameStarted, OnGamePaused, OnGameUnPaused, OnGameEnded, OnPlayerDisconnection, OnStepUpdate, GetLocalData, InputDataProvider);
			}
			return result;
		}

		public AbstractLockstep(float deltaTime, ICommunicator communicator, IPhysicsManagerBase physicsManager, int syncWindow, int panicWindow, int rollbackWindow, TrueSyncEventCallback OnGameStarted, TrueSyncEventCallback OnGamePaused, TrueSyncEventCallback OnGameUnPaused, TrueSyncEventCallback OnGameEnded, TrueSyncPlayerDisconnectionCallback OnPlayerDisconnection, TrueSyncUpdateCallback OnStepUpdate, TrueSyncInputCallback GetLocalData, TrueSyncInputDataProvider InputDataProvider)
		{
			AbstractLockstep.instance = this;
			this.deltaTime = deltaTime;
			this.syncWindow = syncWindow;
			this.panicWindow = panicWindow;
			this.rollbackWindow = rollbackWindow;
			this.totalWindow = syncWindow + rollbackWindow;
			this.StepUpdate = OnStepUpdate;
			this.OnGameStarted = OnGameStarted;
			this.OnGamePaused = OnGamePaused;
			this.OnGameUnPaused = OnGameUnPaused;
			this.OnGameEnded = OnGameEnded;
			this.OnPlayerDisconnection = OnPlayerDisconnection;
			this.GetLocalData = GetLocalData;
			this.InputDataProvider = InputDataProvider;
			this.ticks = 0;
			this.players = new Dictionary<byte, TSPlayer>(INITIAL_PLAYERS_CAPACITY);
			this.activePlayers = new List<TSPlayer>(INITIAL_PLAYERS_CAPACITY);
			this.auxPlayersSyncedData = new List<SyncedData>(INITIAL_PLAYERS_CAPACITY);
			this.auxPlayersInputData = new List<InputDataBase>(INITIAL_PLAYERS_CAPACITY);
			this.communicator = communicator;
			if (communicator != null)
			{
				this.communicator.AddEventListener(new OnEventReceived(this.OnEventDataReceived));
			}
			this.physicsManager = physicsManager;
			this.compoundStats = new CompoundStats();
			this.bufferSyncedInfo = new GenericBufferWindow<SyncedInfo>(SYNCED_INFO_BUFFER_WINDOW);
			this.checksumOk = true;
			this.simulationState = AbstractLockstep.SimulationState.NOT_STARTED;
			this.bodiesToDestroy = new Dictionary<int, List<IBody>>();
			this.playersDisconnect = new Dictionary<int, List<byte>>();
			this.ReplayMode = ReplayRecord.replayMode;
		}

		protected int GetSyncedDataTick()
		{
			return this.ticks - this.syncWindow;
		}

		protected abstract int GetRefTick(int syncedDataTick);

		protected virtual void BeforeStepUpdate(int syncedDataTick, int referenceTick)
		{
		}

		protected virtual void AfterStepUpdate(int syncedDataTick, int referenceTick)
		{
			int i = 0;
			int count = this.activePlayers.Count;
			while (i < count)
			{
				this.activePlayers[i].RemoveData(referenceTick);
				i++;
			}
		}

		protected abstract bool IsStepReady(int syncedDataTick);

		protected abstract void OnSyncedDataReceived(TSPlayer player, List<SyncedData> data);

		protected abstract string GetChecksumForSyncedInfo();

		protected abstract int GetSimulatedTick(int syncedDataTick);

		private void Pause()
		{
			if (this.simulationState == AbstractLockstep.SimulationState.RUNNING)
			{
				this.OnGamePaused();
				this.simulationState = AbstractLockstep.SimulationState.PAUSED;
			}
		}

		private void End()
		{
			if (this.simulationState != AbstractLockstep.SimulationState.ENDED)
			{
				this.OnGameEnded();
				if (this.replayMode == ReplayMode.RECORD_REPLAY)
				{
					ReplayRecord.SaveRecord(this.replayRecord);
				}
				this.simulationState = AbstractLockstep.SimulationState.ENDED;
			}
		}

        #region 生命周期方法
        #region Update 相关
        // 更新
		public void Update()
		{
			if (this.simulationState == AbstractLockstep.SimulationState.WAITING_PLAYERS) // 等待玩家
			{
				this.CheckGameStart();
			}
			else
			{
				if (this.simulationState == AbstractLockstep.SimulationState.RUNNING)
				{
					this.compoundStats.UpdateTime(this.deltaTime);
					if (this.communicator != null)
					{
						this.compoundStats.AddValue("ping", (long)this.communicator.RoundTripTime());
					}
					if (this.syncWindow == 0)
					{
						this.UpdateData();
					}
					int i = 0;
					int num = this.activePlayers.Count;
					while (i < num)
					{
						if (this.CheckDrop(this.activePlayers[i]))
						{
							i--;
							num--;
						}
						i++;
					}
					int syncedDataTick = this.GetSyncedDataTick();
					if (this.CheckGameIsReady() && this.IsStepReady(syncedDataTick))
					{
						this.compoundStats.Increment("simulated_frames");
						this.UpdateData();
						this.elapsedPanicTicks = 0;
						int refTick = this.GetRefTick(syncedDataTick);
						if (refTick > 1 && refTick % 100 == 0)
						{
							this.SendInfoChecksum(refTick);
						}
						this._lastSafeTick = refTick;
						this.BeforeStepUpdate(syncedDataTick, refTick);
						List<SyncedData> tickData = this.GetTickData(syncedDataTick);
						this.ExecutePhysicsStep(tickData, syncedDataTick);
						if (this.replayMode == ReplayMode.RECORD_REPLAY)
						{
							this.replayRecord.AddSyncedData(this.GetTickData(refTick));
						}
						this.AfterStepUpdate(syncedDataTick, refTick);
						this.ticks++;
					}
					else
					{
						if (this.ticks >= this.totalWindow)
						{
							if (this.replayMode == ReplayMode.LOAD_REPLAY)
							{
								this.End();
							}
							else
							{
								this.compoundStats.Increment("missed_frames");
								this.elapsedPanicTicks++;
								if (this.elapsedPanicTicks > this.panicWindow)
								{
									this.compoundStats.Increment("panic");
									if (this.compoundStats.globalStats.GetInfo("panic").count >= 5L)
									{
										this.End();
									}
									else
									{
										this.elapsedPanicTicks = 0;
										this.DropLagPlayers();
									}
								}
							}
						}
						else
						{
							this.compoundStats.Increment("simulated_frames");
							this.physicsManager.UpdateStep();
							this.UpdateData();
							this.ticks++;
						}
					}
				}
			}

		}

        // 检查游戏是否可以开始
        private void CheckGameStart()
        {
            if (this.replayMode == ReplayMode.LOAD_REPLAY) // 加载回放模式
            {
                this.RunSimulation(false);
            }
            else // 非加载回放模式
            {
                bool flag2 = true;
                int i = 0;
                int count = this.activePlayers.Count;
                while (i < count)
                {
                    flag2 &= this.activePlayers[i].sentSyncedStart;
                    i++;
                }
                if (flag2)
                {
                    this.RunSimulation(false);
                    SyncedData.pool.FillStack(this.activePlayers.Count * (this.syncWindow + this.rollbackWindow));
                }
                else
                {
                    this.RaiseEvent(SYNCED_GAME_START_CODE, SyncedInfo.Encode(new SyncedInfo
                    {
                        playerId = this.localPlayer.ID
                    }));
                }
            }
        }
        #endregion Update 相关
        #endregion 生命周期方法

        private bool CheckGameIsReady()
		{
			bool result;
			if (this.GameIsReady != null)
			{
				Delegate[] invocationList = this.GameIsReady.GetInvocationList();
				for (int i = 0; i < invocationList.Length; i++)
				{
					Delegate @delegate = invocationList[i];
					if (!(bool)@delegate.DynamicInvoke(new object[0]))
					{
						result = false;
						return result;
					}
				}
			}
			result = true;
			return result;
		}

		protected void ExecutePhysicsStep(List<SyncedData> data, int syncedDataTick)
		{
			this.ExecutePlayerDisconnects(syncedDataTick);
			this.SyncedArrayToInputArray(data);
			this.StepUpdate(this.auxPlayersInputData);
			this.physicsManager.UpdateStep();
		}

		private void ExecutePlayerDisconnects(int syncedDataTick)
		{
			syncedDataTick++;
			if (this.playersDisconnect.ContainsKey(syncedDataTick))
			{
				List<byte> list = this.playersDisconnect[syncedDataTick];
				int i = 0;
				int count = list.Count;
				while (i < count)
				{
					this.OnPlayerDisconnection(list[i]);
					i++;
				}
			}
		}

        #region 内部方法
        // 更新 非本地玩家的活跃玩家ID 数据
        internal void UpdateActivePlayers()
		{
			this.playersIdsAux.Clear();
			int i = 0;
			int count = this.activePlayers.Count;
			while (i < count)
			{
				if (this.localPlayer == null || this.localPlayer.ID != this.activePlayers[i].ID) // 把被本地玩家加入列表中
				{   
					this.playersIdsAux.Add((int)this.activePlayers[i].ID);
				}
				i++;
			}
			this.auxActivePlayersIds = this.playersIdsAux.ToArray();
		}
        #endregion 内部方法

        

		protected void SyncedArrayToInputArray(List<SyncedData> data)
		{
			this.auxPlayersInputData.Clear();
			int i = 0;
			int count = data.Count;
			while (i < count)
			{
				this.auxPlayersInputData.Add(data[i].inputData);
				i++;
			}
		}

		public void PauseSimulation()
		{
			this.Pause();
			this.RaiseEvent(SIMULATION_CODE, new byte[1], true, this.auxActivePlayersIds);
		}

        // 执行模拟
		public void RunSimulation(bool firstRun)
		{
			this.Run();
			if (!firstRun) // 不是第一次运行
			{
				this.RaiseEvent(SIMULATION_CODE, new byte[]
				{
					SIMULATION_EVENT_RUN
				}, true, this.auxActivePlayersIds);
			}
		}

		public void EndSimulation()
		{
			this.End();
			this.RaiseEvent(SIMULATION_CODE, new byte[]
			{
				SIMULATION_EVENT_END
			}, true, this.auxActivePlayersIds);
		}

		public void Destroy(IBody rigidBody)
		{
			rigidBody.TSDisabled = true;
			int key = this.GetSimulatedTick(this.GetSyncedDataTick()) + 1;
			if (!this.bodiesToDestroy.ContainsKey(key))
			{
				this.bodiesToDestroy[key] = new List<IBody>();
			}
			this.bodiesToDestroy[key].Add(rigidBody);
		}

		protected void CheckSafeRemotion(int refTick)
		{
			if (this.bodiesToDestroy.ContainsKey(refTick))
			{
				List<IBody> list = this.bodiesToDestroy[refTick];
				foreach (IBody current in list)
				{
					bool tSDisabled = current.TSDisabled;
					if (tSDisabled)
					{
						this.physicsManager.RemoveBody(current);
					}
				}
				this.bodiesToDestroy.Remove(refTick);
			}
			if (this.playersDisconnect.ContainsKey(refTick))
			{
				this.playersDisconnect.Remove(refTick);
			}
		}

		private void DropLagPlayers()
		{
			List<TSPlayer> list = new List<TSPlayer>();
			int refTick = this.GetRefTick(this.GetSyncedDataTick());
			if (refTick >= 0)
			{
				int i = 0;
				int count = this.activePlayers.Count;
				while (i < count)
				{
					TSPlayer tSPlayer = this.activePlayers[i];
					if (!tSPlayer.IsDataReady(refTick))
					{
						tSPlayer.dropCount++;
						list.Add(tSPlayer);
					}
					i++;
				}
			}
			int j = 0;
			int count2 = list.Count;
			while (j < count2)
			{
				TSPlayer p = list[j];
				this.CheckDrop(p);
				bool sendDataForDrop = list[j].GetSendDataForDrop(this.localPlayer.ID, this._syncedDataCacheDrop);
				if (sendDataForDrop)
				{
					this.communicator.OnRaiseEvent(SEND_CODE, SyncedData.Encode(this._syncedDataCacheDrop), true, null);
					SyncedData.pool.PutBack(this._syncedDataCacheDrop[0]);
				}
				j++;
			}
		}

		private SyncedData UpdateData()
		{
			SyncedData result;
			if (this.replayMode == ReplayMode.LOAD_REPLAY) // 加载回放模式
			{
				result = null;
			}
            else // 非加载回放模式
			{
				SyncedData @new = SyncedData.pool.TakeUp(); // 从池中获取
				@new.Init(this.localPlayer.ID, this.ticks);
                this.GetLocalData(@new.inputData); // 把玩家操作数据放入SyncedData实例中
                this.localPlayer.AddData(@new); // 把SyncedData实例放入本地玩家的操作数据字典中

				if (this.communicator != null) // 有通信器
				{
                    this.localPlayer.GetSendData(this.ticks, this._syncedDataCacheUpdateData); // 把最新的操作数据放入_syncedDataCacheUpdateData中
					this.communicator.OnRaiseEvent(SEND_CODE, SyncedData.Encode(this._syncedDataCacheUpdateData), true, this.auxActivePlayersIds); // 发送操作数据
				}
				result = @new;
			}
			return result;
		}

		public InputDataBase GetInputData(int playerId)
		{
			return this.players[(byte)playerId].GetData(this.GetSyncedDataTick()).inputData;
		}

		private void SendInfoChecksum(int tick)
		{
			if (this.replayMode != ReplayMode.LOAD_REPLAY)
			{
				SyncedInfo syncedInfo = this.bufferSyncedInfo.Current();
				syncedInfo.playerId = this.localPlayer.ID;
				syncedInfo.tick = tick;
				syncedInfo.checksum = this.GetChecksumForSyncedInfo();
				this.bufferSyncedInfo.MoveNext();
				this.RaiseEvent(CHECKSUM_CODE, SyncedInfo.Encode(syncedInfo));
			}
		}

		private void RaiseEvent(byte eventCode, object message)
		{
			this.RaiseEvent(eventCode, message, true, null);
		}

		private void RaiseEvent(byte eventCode, object message, bool reliable, int[] toPlayers)
		{
			if (this.communicator != null)
			{
				this.communicator.OnRaiseEvent(eventCode, message, reliable, toPlayers);
			}
		}

		private void OnEventDataReceived(byte eventCode, object content)
		{
			if (eventCode == SEND_CODE) // 接收操作数据
			{
				byte[] data = content as byte[];
				List<SyncedData> list = SyncedData.Decode(data); // 解码
				if (list.Count > 0) // 有数据
				{
					TSPlayer tSPlayer = this.players[list[0].inputData.ownerID]; // 找到对应的玩家
					if (!tSPlayer.dropped) // 该玩家没有掉线
					{
						this.OnSyncedDataReceived(tSPlayer, list); // 处理同步数据
						if (list[0].dropPlayer && tSPlayer.ID != this.localPlayer.ID && !this.players[list[0].dropFromPlayerId].dropped)
						{
							tSPlayer.dropCount++; // 统计掉线玩家数量
						}
					}
					else // 该玩家掉线了,回收数据对象
					{
						int i = 0;
						int count = list.Count;
						while (i < count)
						{
							SyncedData.pool.PutBack(list[i]);
							i++;
						}
					}
					SyncedData.poolList.PutBack(list);
				}
			}
			else
			{
				if (eventCode == CHECKSUM_CODE)
				{
					byte[] infoBytes = content as byte[];
					this.OnChecksumReceived(SyncedInfo.Decode(infoBytes));
				}
				else
				{
					if (eventCode == SIMULATION_CODE)
					{
						byte[] array = content as byte[];
						if (array.Length != 0)
						{
							if (array[0] == SIMULATION_EVENT_PAUSE)
							{
								this.Pause();
							}
							else if (array[0] == SIMULATION_EVENT_RUN)
							{
								this.Run();
							}
							else if (array[0] == SIMULATION_EVENT_END)
							{
								this.End();
							}
						}
					}
					else
					{
						if (eventCode == SYNCED_GAME_START_CODE)
						{
							byte[] infoBytes2 = content as byte[];
							SyncedInfo syncedInfo = SyncedInfo.Decode(infoBytes2);
							this.players[syncedInfo.playerId].sentSyncedStart = true;
						}
					}
				}
			}
		}

		private void OnChecksumReceived(SyncedInfo syncedInfo)
		{
			if (!this.players[syncedInfo.playerId].dropped)
			{
				this.checksumOk = true;
				SyncedInfo[] buffer = this.bufferSyncedInfo.buffer;
				for (int i = 0; i < buffer.Length; i++)
				{
					SyncedInfo syncedInfo2 = buffer[i];
					if (syncedInfo2.tick == syncedInfo.tick && syncedInfo2.checksum != syncedInfo.checksum)
					{
						this.checksumOk = false;
						break;
					}
				}
			}
		}

		protected List<SyncedData> GetTickData(int tick)
		{
			this.auxPlayersSyncedData.Clear();
			int i = 0;
			int count = this.activePlayers.Count;
			while (i < count)
			{
				this.auxPlayersSyncedData.Add(this.activePlayers[i].GetData(tick));
				i++;
			}
			return this.auxPlayersSyncedData;
		}

        // 添加玩家
		public void AddPlayer(byte playerId, string playerName, bool isLocal)
		{
			TSPlayer tSPlayer = new TSPlayer(playerId, playerName);
			this.players.Add(tSPlayer.ID, tSPlayer);
			this.activePlayers.Add(tSPlayer);
			if (isLocal) // 本地玩家
			{
				this.localPlayer = tSPlayer;
				this.localPlayer.sentSyncedStart = true;
			}
			this.UpdateActivePlayers();
			if (this.replayMode == ReplayMode.RECORD_REPLAY)
			{
				this.replayRecord.AddPlayer(tSPlayer);
			}
		}

		private bool CheckDrop(TSPlayer p)
		{
			bool result;
			if (p != this.localPlayer && !p.dropped && p.dropCount > 0)
			{
				int num = this.activePlayers.Count - 1;
				if (p.dropCount >= num)
				{
					this.compoundStats.globalStats.GetInfo("panic").count = 0L;
					p.dropped = true;
					this.activePlayers.Remove(p);
					this.UpdateActivePlayers();
					Debug.Log("Player dropped (stopped sending input)");
					int key = this.GetSyncedDataTick() + 1;
					if (!this.playersDisconnect.ContainsKey(key))
					{
						this.playersDisconnect[key] = new List<byte>();
					}
					this.playersDisconnect[key].Add(p.ID);
					result = true;
					return result;
				}
			}
			result = false;
			return result;
        }

        #region 私有方法
        // 运行
        private void Run()
        {
            if (this.simulationState == AbstractLockstep.SimulationState.NOT_STARTED) // 未开始
            {
                this.simulationState = AbstractLockstep.SimulationState.WAITING_PLAYERS;
            }
            else // 非NOT_STARTED状态
            {
	            if (this.simulationState == AbstractLockstep.SimulationState.WAITING_PLAYERS) // 正在等待玩家
	            {
		            this.OnGameStarted();
		            this.simulationState = AbstractLockstep.SimulationState.RUNNING; // 改为运行状态
	            }
	            else if( this.simulationState == AbstractLockstep.SimulationState.PAUSED) // 暂停状态
	            {
		            this.OnGameUnPaused();
		            this.simulationState = AbstractLockstep.SimulationState.RUNNING; // 改为运行状态
	            }
            }
        }
        #endregion 私有方法
    }
}
